import { Request, Response, NextFunction} from 'express';
import axios from 'axios';
import crypto from 'crypto';
import { v4 as uuidv4 } from 'uuid';
import { getPayments, paymentRecived, getPrice, getOrder, getTarget_prices, createTargetPrice, updateTargetPrice, deleteTargetType, byKeyWordSelect } from './order.service';
import { getOpenIdByOpenId } from '../user/user.service';
import { httpClient, logger, uid, xmlBuilder, xmlParser } from "../app/app.service";
import * as configParam from "../app/app.config";
import { wxpayVerifyPaymentResult } from './wxpay/wxpay.service'

/**
 * 配置参数
 */
const URL = configParam.WXPAY_API_UNIFIEDORDER;
const appid = configParam.WXPAY_APP_ID;
const mch_id = configParam.WXPAY_MCH_ID;
const wxpayKey = configParam.WXPAY_KEY;
const notify_url = configParam.WXPAY_NOTIFY_URL;
const trade_type = 'JSAPI';
const signType = 'MD5';

/**
 * 支付
 */
export const payment = async (
  request: Request,
  response: Response,
  next: NextFunction
) => {
  const out_trade_no = uuidv4().replace(/-/g, "").toUpperCase();
  const nonce_str = uuidv4().replace(/-/g, "").toUpperCase();
  const timeStamp = Math.round(new Date().getTime() / 1000).toString();
  
  const userId = request.user.id;
  const { commodity_type } = request.body;
  let total_fee = request.body.price;
  const body = request.body.bodyCon;
  const userAtuth = await getOpenIdByOpenId(userId);
  const openid = userAtuth.openid
  const spbill_create_ip = request.ip

  const XML = demoCreateSign(
    appid,
    body,
    mch_id,
    nonce_str,
    notify_url,
    openid,
    out_trade_no,
    spbill_create_ip,
    total_fee,
    trade_type
  );

  try {
    axios({
      method: 'POST',
      url: URL,
      data: XML.CRxml
    }).then( async (res) => {
      
      const prepayResult = await xmlParser.parseStringPromise(res.data);
      let xml = prepayResult.xml;
      let pkg = "prepay_id=" + xml.prepay_id
      const faqianduan = twoSign(
        appid,
        timeStamp,
        nonce_str,
        pkg,
        signType
      )

      let data = {
        appId: appid,
        openid: openid,
        timeStamp: timeStamp,
        nonceStr: nonce_str,
        paySign: faqianduan,
        package: "prepay_id=" + xml.prepay_id,
        signType: signType
      }
      
      // 创建订单
      const orderData = { user_id: userId, total_fee: total_fee, commodity_type: commodity_type, out_trade_no: out_trade_no }
      await creatOrder(orderData);

      response.send(data);
    })
  } catch (error) {
    next(error);
  }
}

/**
 * 生成签名
 */
const demoCreateSign = (
  appid: string,
  body: string,
  mch_id: string,
  nonce_str: string,
  notify_url: string,
  openid: string,
  out_trade_no: string,
  spbill_create_ip: string,
  total_fee: number,
  trade_type: string
  ) => {

  let stringA = "appid=" + appid
    + "&body=" + body
    + "&mch_id=" + mch_id
    + "&nonce_str=" + nonce_str
    + "&notify_url=" + notify_url
    + "&openid=" + openid
    + "&out_trade_no=" + out_trade_no
    + "&spbill_create_ip=" + spbill_create_ip
    + "&total_fee=" + total_fee
    + "&trade_type=" + trade_type
    + "&key=" + wxpayKey
  let sign = crypto.createHash("MD5").update(stringA).digest('hex').toUpperCase();
  
  let CRxml = `
    <xml>
      <appid>${appid}</appid>
      <body>${body}</body>
      <mch_id>${mch_id}</mch_id>
      <nonce_str>${nonce_str}</nonce_str>
      <notify_url>${notify_url}</notify_url>
      <openid>${openid}</openid>
      <out_trade_no>${out_trade_no}</out_trade_no>
      <spbill_create_ip>${spbill_create_ip}</spbill_create_ip>
      <total_fee>${total_fee}</total_fee>
      <trade_type>${trade_type}</trade_type>
      <sign>${sign}</sign>
    </xml>
  `

  let cellData = { CRxml, sign }
  
  return cellData;
}

/**
 * 二次签名
 */
let twoSign = (
  appid: string,
  timeStamp: string,
  nonce_str: string,
  pkg: string,
  signType: string
) => {
  let stringB = "appId=" + appid
  + "&nonceStr=" + nonce_str
  + "&package=" + pkg
  + "&signType=" + signType
  + "&timeStamp=" + timeStamp
  + "&key=" + wxpayKey
  let signTwo_2 = crypto.createHash("MD5").update(stringB).digest('hex').toUpperCase();
  
  return signTwo_2;
}

/**
 * 创建订单
 */
const creatOrder = async (orderData: any) => {
  orderData.releaseTime = Math.floor(new Date().getTime());

  const data = await getPayments(orderData);

  return data;
};

/**
 * 支付通知
 */

/**
 * 微信支付结果通知
 */
export interface WxpayPaymentResult {
  appid: string;
  bank_type: string;
  cash_fee: number;
  fee_type: string;
  is_subscribe: string;
  mch_id: string;
  nonce_str: string;
  openid?: string;
  out_trade_no: string;
  result_code: string;
  return_code: string;
  sign: string;
  time_end: string;
  total_fee: number;
  trade_type: string;
  transaction_id: string;
}

export const notify = async (
  request: Request,
  response: Response,
  next: NextFunction
) => {

  try {
    // 1. 处理通知数据
    if (!request.body) throw new Error('BAD_REQUEST');
    
    const dataXML = await xmlParser.parseStringPromise(request.body);
    const paymentResult = dataXML.xml as WxpayPaymentResult;

    logger.info('微信支付结果：', paymentResult);

    // 找出相关订单
    const orderId = paymentResult.out_trade_no;

    // 2. 验证通知数据
    const isValid = await wxpayVerifyPaymentResult(paymentResult);

    // 3. 处理完成付款
    if (isValid) {
      paymentRecived(orderId, paymentResult);
    }

    // 4. 作出响应
    const return_code = isValid ? 'SUCCESS' : 'FAIL';

    const responseData = xmlBuilder.buildObject({
      xml: { return_code },
    });

    response.header({'Content-Type': 'text/xml'}).send(responseData);
  } catch (error) {
    next(error);
  }
};

/**
 * 获取订单
 */
export const orders = async (
  request: Request,
  response: Response,
  next: NextFunction
) => {
  let pageNo = request.query.pageNo
  let status = request.query.status
  let user_id = request.query.userId

  try {
    const data = await getOrder(10, parseInt(`${pageNo}`, 10), parseInt(`${status}`, 10), parseInt(`${user_id}`, 10));
    response.send(data);
  } catch (error) {
    next(error);
  }
};

/**
 * 获取价格
 */
export const target_prices = async (
  request: Request,
  response: Response,
  next: NextFunction
) => {
  try {
    const data = await getTarget_prices();
    
    // 做出响应
    response.send(data);
  } catch (error) {
    next(error);
  }
}

/**
 * 创建类目价格
 */
export const storeTargetPrice = async (
  request: Request,
  response: Response,
  next: NextFunction
) => {
  // 获取类目
  const { target_type } = request.body;

  // 获取价格
  const { price } = request.body;

  let releaseTime = Math.floor(new Date().getTime())
  const targetPriceModel = request.body;
  targetPriceModel.releaseTime = releaseTime;

  try {
    if (targetPriceModel.id && targetPriceModel.id > 0) {
      const data = await updateTargetPrice(targetPriceModel.id, targetPriceModel);

      // 做出响应
      response.send(data);
    } else {
      const data = await createTargetPrice(targetPriceModel);
      // 做出响应
      response.send(data);
    }
    
  } catch (error) {
    next(error);
  }
};

/**
 * 更新类目价格
 */
export const updateTarget = async (
  request: Request,
  response: Response,
  next: NextFunction
) => {
  // 获取类目
  const { id } = request.body;

  // 获取价格
  const { price } = request.body;

  try {
    const data = await updateTargetPrice(price, id);

    // 做出响应
    response.send(data);
  } catch (error) {
    next(error);
  }
};

/**
 * 删除类目价格
 */
export const destroyTargetPrice = async (
  request: Request,
  response: Response,
  next: NextFunction
) => {
  // 获取类目
  const { postId } = request.body;

  try {
    const data = await deleteTargetType(parseInt(postId, 10));
    response.send(data);
  } catch (error) {
    next(error);
  }
};

/**
 * 模糊查询
 */
export const selectByKey = async (
  request: Request,
  response: Response,
  next: NextFunction
) => {
  // 获取关键字
  const { keyWord } = request.query;

  try {
    const data = await byKeyWordSelect(keyWord)
    response.send(data)
  } catch (error) {
    next(error)
  }
}
